using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Xml;

using Skeletons;

namespace Rigging
{
    /// <summary>
    /// Questa classe serve solo in fase di caricamento, assiste nell'associare una determinata "pelle"
    /// ad un dato scheletro
    /// </summary>
    public class SkinLoader
    {
        public SkinLoader()
        {
        }

        /// <summary>
        /// Carica il set di Skin contenuto nell'XML, nello scheletro specificato. Il match viene eseguito in base al
        /// nome degli arti specificato in fase di design.
        /// </summary>
        /// <param name="skel">Lo scheletro da rivestire</param>
        /// <param name="xml">Il nodo XML estratto dal file di progetto</param>
        public void LoadSkins(Skeletons.Skeleton skel, XmlNode skinNode)
        {
            int i = 0;
            try
            {
                // prima ripuliamo lo scheletro da tutte le skin
                skel.DeleteAllSkins();

                // poi lo rivestiamo
                XmlNodeList skins = skinNode.SelectNodes("SKIN");
                foreach (XmlNode s in skins)
                {
                    string boneId = s.Attributes["boneId"].Value;
                    Bone found = skel.Bones.Find(delegate(Bone b) { return b.Id == boneId; });
                    if (found != null)
                    {
                        if (found.Skin == null)
                        {
                            found.Skin = new Skin();
                            found.Skin.Filled = true;
                        }
                        RiggingEntity anew = Skin.CreateFromXml(skel, s);
                        found.Skin.AddRigging(anew);
                    }
                }
            }
            catch (Exception ex)
            {
                System.Console.WriteLine("Errore LoadSkins(): " + ex.Message);
            }
        }
    }


    /// <summary>
    /// Questa classe incapsula la List<RiggingEntity>, aggiungendo metodi utili quali SelectAll() e UnselectAll()
    /// </summary>
    public class RiggingEntityCollection : List<RiggingEntity>
    {
        public void SelectAll()
        {
            base.ForEach(delegate(RiggingEntity et) { et.Selected = true; });
        }

        public void UnselectAll()
        {
            base.ForEach(delegate(RiggingEntity et) { et.Selected = false; });
        }
    }


    /// <summary>
    /// Questa classe contiene l'insieme di percorsi grafici (bline, poligoni freehand o altro) associati ad un singolo Bone
    /// </summary>
    public class Skin
    {
        private Region m_region;
        //private List<RiggingEntity> m_entities;
        private RiggingEntityCollection m_entities;
        private Pen m_pen = Pens.Black;
        private bool m_filled = false;
        private bool m_selected = false;

		/// <summary>
		/// Temporary BLine: used while "creating" it with the mouse.
		/// Once the BLine is completly created it gets attached to m_entities
		/// </summary>
		private BLine m_oTemporaryBLine = null;
		
		/// <summary>
		/// Temporary SPLine: used while "creating" it with the mouse.
		/// Once the SPLine is completly created it gets attached to m_entities
		/// </summary>
		private SPLine m_oTemporarySPLine = null;

		/// <summary>
		/// Temporary BLine: used while "creating" it with the mouse.
		/// Once the BLine is completly created it gets attached to m_entities
		/// </summary>
		public BLine TemporaryBLine
		{
			get { return m_oTemporaryBLine; }
			set { m_oTemporaryBLine = value; }
		}

		/// <summary>
		/// Temporary SPLine: used while "creating" it with the mouse.
		/// Once the SPLine is completly created it gets attached to m_entities
		/// </summary>
		public SPLine TemporarySPLine
		{
			get { return m_oTemporarySPLine; }
			set { m_oTemporarySPLine = value; }
		}

        //public List<RiggingEntity> Entities
        public RiggingEntityCollection Entities
        {
            get { return m_entities; }
        }

		public void UnselectAll()
		{
			if(m_oTemporaryBLine != null)
				m_oTemporaryBLine = null;
			foreach(RiggingEntity e in m_entities)
				e.Selected = false;
		}

        public bool Selected
        {
            get { return m_selected; }
            set {
                foreach (RiggingEntity r in m_entities)
                    r.Selected = value;
                m_selected = value; 
            }
        }

        public bool Filled
        {
            get { return m_filled; }
            set { m_filled = value; }
        }

        public Pen Pen
        {
            get { return m_pen; }
            set { m_pen = value; }
        }
        private int m_width = 1;

        public Skin()
        {
            m_entities = new RiggingEntityCollection();
        }

        public void AddRegion(Region reg)
        {
        }

        public void AddPath(RiggingEntity path)
        {
            m_entities.Add(path);
        }

        public void AddArc(Point center, int radius, int start, int sweep)
        {
        }

        public void AddFreehand(FreeHand fh)
        {

        }

		public RiggingEntity HitTest(Point pt, int treshold)
		{
			foreach(RiggingEntity e in m_entities)
			{
				if(e.IsUnder(pt, treshold))
					return e;
			}
			return null;
		}

        public bool ContainedByRect(Matrix mt, Rectangle rect) 
        { 
            foreach(RiggingEntity r in m_entities)
                if( !r.ContainedByRect(mt, rect ))
                    return false;
            return true;
        }

        public void LoadSample(Point p)
        {
            //return;

            // Create an array of points for the curve in the second figure.
            Point[] points = {
                new Point(40, 60),
                new Point(50, 70),
                new Point(30, 90)
            };

            GraphicsPath path = new GraphicsPath();

            path.StartFigure(); // Start the first figure.
            path.AddArc(175, 50, 50, 50, 0, -180);
            path.AddLine(100, 0, 250, 20);
            // First figure is not closed.

            path.StartFigure(); // Start the second figure.
            path.AddLine(50, 20, 5, 90);
            path.AddCurve(points, 3);
            path.AddLine(50, 150, 150, 180);
            path.CloseFigure(); // Second figure is closed.

            //m_paths.Add(path);
            //e.Graphics.DrawPath(new Pen(Color.FromArgb(255, 255, 0, 0), 2), path);
            Matrix mt = new Matrix();
            mt.Translate(p.X, p.Y);
            path.Transform(mt);
        }

        public void Repaint(Graphics gr)
        {
            foreach (RiggingEntity pt in m_entities)
            {
                //System.Console.WriteLine("Repainting path");
                pt.Repaint(gr);
            }
            
            if(m_oTemporaryBLine != null)
				m_oTemporaryBLine.Repaint(gr);
			if(m_oTemporarySPLine != null)
				m_oTemporarySPLine.Repaint(gr);
        }

        internal void AddRigging(RiggingEntity graph)
        {
            m_entities.Add(graph);
        }

        internal string ToXmlString(string indent)
        {
            string ret = "";
            foreach(RiggingEntity ent in m_entities)
                ret += ent.ToXmlString(indent);
            return ret;
        }

        internal static RiggingEntity CreateFromXml(Skeleton skel, XmlNode b)
        {
            string type = b.Attributes["type"].Value;
            string boneId = b.Attributes["boneId"].Value;
            string width = b.Attributes["penWidth"].Value;
            string color = b.Attributes["color"].Value;
            //string filled = b.Attributes["filled"].Value;
            RiggingEntity ent = null;
            Bone bone = skel.Bones.Find(delegate(Bone bn) { return bn.Id == boneId; });

            Color cl = new Color();
            if (color.StartsWith("Color [A="))
            {
                cl = ParseArgbColorDef(color);
            }
            else if(color.StartsWith("Color ["))
            {
                cl = Color.FromName(color.Replace("Color [","").Replace("]",""));
            }

            switch (type)
            {
                case "BLine":
                    ent = new BLine(bone, Color.Black, cl, int.Parse(width));
                    XmlNodeList list = b.SelectNodes("POINT");
                    Point[] pts = new Point[list.Count];
                    int i = 0;
                    foreach (XmlNode nd in list)
                        pts[i++] = new Point(int.Parse(nd.Attributes["X"].Value), int.Parse(nd.Attributes["Y"].Value));
                    ent.LoadPoints(pts);
                    break;
				case "SPLine":
					ent = new SPLine(bone, Color.Black, cl, int.Parse(width));
					list = b.SelectNodes("POINT");
					pts = new Point[list.Count];
					i = 0;
					foreach (XmlNode nd in list)
						pts[i++] = new Point(int.Parse(nd.Attributes["X"].Value), int.Parse(nd.Attributes["Y"].Value));
					ent.LoadPoints(pts);
					break;
                case "FreeHand":
                    ent = new FreeHand(bone, new Pen(cl), int.Parse(width));
                    list = b.SelectNodes("POINT");
                    pts = new Point[list.Count];
                    i = 0;
                    foreach (XmlNode nd in list)
                        pts[i++] = new Point(int.Parse(nd.Attributes["X"].Value), int.Parse(nd.Attributes["Y"].Value));
                    ent.LoadPoints(pts);
                    break;
                default:
                    break;
            }
            ent.Filled = true;

            //newSkin.AddRigging(ent);
            //return newSkin;
            return ent;
        }

        private static Color ParseArgbColorDef(string color)
        {
            System.Text.RegularExpressions.Regex rx = new System.Text.RegularExpressions.Regex(@"Color \[A=(?<A>\d+), R=(?<R>\d+), G=(?<G>\d+), B=(?<B>\d+)");
            System.Text.RegularExpressions.Match mt = rx.Match(color);
            if(mt.Success)
            {
                int a = int.Parse(mt.Groups["A"].Value);
                int r = int.Parse(mt.Groups["R"].Value);
                int g = int.Parse(mt.Groups["G"].Value);
                int b = int.Parse(mt.Groups["B"].Value);
                return Color.FromArgb(a,r,g,b);
            }
            return Color.Black;
        }

    }

    public abstract class RiggingEntity
    {
        protected Skeletons.Bone m_bone;
        public abstract void Repaint(Graphics gr);
        public abstract string ToXmlString(string indent);
        public abstract void LoadPoints(Point[] pts);
        protected bool m_selected = false;
		public abstract bool IsUnder(Point p, int treshold);
        private bool m_filled = false;
        protected List<Point> m_points = new List<Point>();
        protected Pen m_penColor = Pens.Black;
        protected Pen m_outlineColor = Pens.Black;
        private string m_id;
        private float m_zdepth;

        public float ZDepth
        {
            get { return m_zdepth; }
            set { m_zdepth = value; }
        }

        public Skeletons.Bone Bone
        {
            get { return m_bone; }
        }

        public string Id
        {
            get { return m_id; }
            set { m_id = value; }
        }

        public Pen OutlineColor
        {
            get { return m_outlineColor; }
            set { m_outlineColor = value; }
        }

        public Pen PenColor
        {
            get { return m_penColor; }
            set { m_penColor = value; }
        }
        protected DashStyle m_penStyle = DashStyle.Solid;

        public DashStyle PenStyle
        {
            get { return m_penStyle; }
            set { m_penStyle = value; }
        }

        public bool Filled
        {
            get { return m_filled; }
            set { m_filled = value; }
        }

        public virtual bool Selected
        {
            get { return m_selected; }
            set { m_selected = value; }
        }

        public List<Point> Points
        {
            get { return m_points; }
            set { m_points = value; }
        }

        public abstract bool ContainedByRect(Matrix mt, Rectangle r);

        protected void PaintHandle(Graphics gr, Point p)
        {
            gr.DrawRectangle(Pens.Red, p.X - 2, p.Y - 2, 4, 4);     // square shape
            //gr.DrawArc(Pens.Yellow, p.X - 2, p.Y - 2, 4, 4,0, 360);   // circular shape
        }

    }

    public class FreeHand : RiggingEntity
    {
        private PenType m_fillStyle = PenType.SolidColor;
        private float m_penWidth = 1F;

        public override bool IsUnder(Point p,int treshold)
        {
            foreach(Point pt in m_points)
            {
				if (Math.Abs(pt.X - p.X) > treshold)
					continue;
				if (Math.Abs(pt.Y - p.Y) <= treshold)
					return true;
				
			}
			return false;
        }

        public FreeHand(Skeletons.Bone parent, Pen color, float width)
        {
            m_bone = parent;
            m_penColor = color;
            m_penWidth = width;
        }

        public void AddPoint(Point p)
        {
            m_points.Add(p);
        }


        /// <summary>
        /// Carica l'oggetto FreeHand con l'array di punti specificato
        /// </summary>
        /// <param name="pts">I punti del tracciato FreeHand</param>
        public override void LoadPoints(Point[] pts)
        {
            m_points = new List<Point>(pts);
        }

        public override void Repaint(Graphics gr)
        {
            bool shown = false;
            int index = 0;
            Point prev = new Point(0,0);
            Pen pen = new Pen(m_outlineColor.Color, m_penWidth);
            foreach (Point p in m_points)
            {
                if (index != 0)
                {
                    gr.DrawLine(pen, prev, p);
                }
                prev = p;
                index++;
            }

            if (Filled)
            {
                try
                {
                    //System.Console.WriteLine("Freehand.Repaint(): " + m_penColor.Brush.ToString());
                    //m_penColor = Pens.Beige;
                    gr.FillPolygon(m_penColor.Brush, m_points.ToArray());
                }
                catch(Exception e)
                {
                    shown = true;
                    if( !shown )
                        MessageBox.Show("Freehand.Repaint(): Errore nel riempimento del poligono:\n" + e.Message, "Errore interno");
                }
            }

            if (m_selected)
            {
                foreach (Point p in m_points)
                {
                    PaintHandle(gr, p);
                }
            }
        }

        public override bool ContainedByRect(Matrix mt, Rectangle r)
        {
            if (m_points.Count > 0)
            {
                Point[] pts = m_points.ToArray();

                mt.TransformPoints(pts);
                foreach (Point p in pts)
                {
                    if (!r.Contains(p))
                        return false;
                }
                return true;
            }
            else
                return false;
        }

        internal Point GetLastPoint()
        {
            if (m_points.Count > 0)
                return m_points[m_points.Count - 1];
            else
                return new Point(-5,-5);
        }

        public override string ToXmlString(string indent)
        {
            string color = m_penColor.Color.ToString(); //.Replace("Color [","").Replace("]","");
            string ret = indent + "<SKIN type='FreeHand' boneId='" + Bone.Id + "' color='" + color + "' penWidth='" + m_penWidth + "' >\n";
            for (int i = 0; i < m_points.Count; i++ )
                ret += indent + "\t<POINT index='" + i + "' X='" + m_points[i].X + "' Y='" + m_points[i].Y + "'/>\n";
            ret += indent + "</SKIN>\n";

            return ret;
        }
    }



}
